import { isObjectValueEqual } from '@/utils/obj/equal';
import { isEmpty } from 'lodash-es';
import {
  useContext,
  useEffect,
  useLayoutEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import DndContext from '../../context/DndContext';
import DropTargetImpl from '../classes/DropTargetImpl';
import DropTargetMonitorImpl from '../classes/DropTargetMonitorImpl';
import type {
  DropTarget,
  DropTargetHookSpec,
  DropTargetMonitor,
  FactoryOrInstance,
} from '../dropInterface';

export function useDrop<
  DragItem = unknown,
  DropResult = unknown,
  CollectedProps = unknown,
>(
  specArg: FactoryOrInstance<
    DropTargetHookSpec<DragItem, DropResult, CollectedProps>
  >,
  deps?: unknown[],
): [CollectedProps] {
  const lastConnectdropNodeRef = useRef<HTMLElement>();
  const [targetId, setTargetId] = useState<string>();
  const dropNodeUnBindEventsFnRef = useRef<Function>();
  const [selectedProps, setSelectedProps] = useState<CollectedProps>(
    {} as CollectedProps,
  );

  const { dragDropManager } = useContext(DndContext);
  const monitorRef = useRef<DropTargetMonitor<DragItem, CollectedProps>>();
  const dropTargetRef = useRef<DropTarget>();

  const memoDeps = [...(deps || [])];
  if (!deps && typeof specArg !== 'function') {
    memoDeps.push(specArg);
  }

  const spec = useMemo(() => {
    if (typeof specArg === 'function') return specArg();
    return specArg;
  }, memoDeps);

  useLayoutEffect(() => {
    const { accept } = spec ?? {};
    if (isEmpty(accept)) {
      throw new Error('useDrop:请设置accept');
    }
    if (!dragDropManager) return;
    const monitor = new DropTargetMonitorImpl(dragDropManager); //2.当前 DropTarget对应的 监听器实例
    monitorRef.current = monitor;

    const dropTarget = new DropTargetImpl<DragItem, DropResult, CollectedProps>(
      spec,
      monitor,
    ); // 3.生成放置源实例
    dropTargetRef.current = dropTarget;

    const registry = dragDropManager.getRegistry();
    const targetId = registry.addTarget(spec.accept, dropTarget); //4.注册drop源实例到注册表中
    setTargetId(targetId);
    monitor.receiveHandlerId(targetId);
    // // console.log('--types--dropTargets--:',registry.types,registry.dropTargets);

    return () => {
      if (typeof dropNodeUnBindEventsFnRef.current === 'function') {
        dropNodeUnBindEventsFnRef.current(); //dropNode取消绑定drag、drop事件
        lastConnectdropNodeRef.current = undefined;
      }
      if (targetId) {
        registry.removeTarget(targetId);
      }
    };
  }, []);

  useEffect(() => {
    if (!dragDropManager || !monitorRef.current || !dropTargetRef.current)
      return;
    const newSpec = { ...spec };
    dropTargetRef.current.setSpec(newSpec);
    const { accept, dropNode, collect } = newSpec ?? {};
    if (!isEmpty(accept) && dropNode && targetId) {
      if (
        !lastConnectdropNodeRef.current ||
        lastConnectdropNodeRef.current !== dropNode
      ) {
        if (typeof dropNodeUnBindEventsFnRef.current === 'function') {
          dropNodeUnBindEventsFnRef.current();
        }
        lastConnectdropNodeRef.current = dropNode;
        dropNodeUnBindEventsFnRef.current =
          dragDropManager.bindEventsForDropNode(targetId, dropNode); // 5.dropNode绑定drag、drop事件
        if (typeof collect === 'function') {
          monitorRef.current.subscribeStateChange(() => {
            //6.当前正在拖拽的数据（redux）变化时更新collect函数的返回值
            // setTimeout(() => {
            const newValue = collect(monitorRef.current!);
            if (!isObjectValueEqual(newValue, selectedProps)) {
              setSelectedProps({ ...newValue });
            }
            // })
          });
        }
      }
    }
  }, [spec, targetId]);

  return [selectedProps];
}
